package ca.tatham.senssim;

import java.io.BufferedWriter;
import java.io.IOException;
import java.text.DecimalFormat;

import org.apache.log4j.Logger;

import ca.tatham.senssim.diffusion.AbstractDiffusionNode;
import ca.tatham.senssim.diffusion.AbstractSinkNode;
import ca.tatham.senssim.diffusion.DataMessage;
import ca.tatham.senssim.pddd.SimpleNodePddd;

public class Statistics
{
  public final static DecimalFormat FORMAT = new DecimalFormat("0.00");

  private final int m_density;

  private long m_nodes = 0;
  private long m_sinks = 0;
  private long m_sources = 0;
  private long m_totalEnergy = 0;
  private long m_totalInterestsSentAtSink = 0;
  private long m_totalDataSentAtSource = 0;
  private long m_totalDataReceivedAtSink = 0;
  private long m_sumOfLatency = 0;

  private final long m_startTime;
  private long m_runTime;

  private long m_ackOverhead;

  public Statistics(int density)
  {
    m_density = density;
    m_startTime = System.currentTimeMillis();
  }

  public void log(Logger log)
  {
    log.info("Simulation Run Time: " + m_runTime + "ms");
    log.info("Total Interest Packets Sent at Sinks: "
        + m_totalInterestsSentAtSink);
    log.info("Total Data Packets Sent at Sensors: " + m_totalDataSentAtSource);
    log.info("Total Data Packets Received at Sinks: "
        + m_totalDataReceivedAtSink);
    log.info("Total Energy Consumed: " + m_totalEnergy);
    log.info("Average Latency: " + getAverageLatency());
    log.info("Packet Delivery Ratio: " + getPacketDeliveryRatio());
    log.info("Average Dissipated Energy per received packet per node: "
        + getAverageDissipatedEnergyPerReceivedPacketPerNode());
    log.info("Ack Overhead: " + m_ackOverhead);
  }

  public void writeHeader(BufferedWriter out) throws IOException
  {
    out
        .write("#Nodes,Density,RunTime,Data Sent,Data Received,Avg Latency,Packet Delivery Ratio,Avg Dissipated Energy Per Received Data Per Node");
    out.newLine();
  }

  public void writeData(BufferedWriter out) throws IOException
  {
    out.write(m_nodes + "," + getDensity() + "," + m_runTime + ","
        + m_totalDataSentAtSource + "," + m_totalDataReceivedAtSink + ","
        + getAverageLatency() + "," + getPacketDeliveryRatio() + ","
        + getAverageDissipatedEnergyPerReceivedPacketPerNode() + ","
        + getAckOverhead());
    out.newLine();
    out.flush();
  }

  private long getAckOverhead()
  {
    return m_ackOverhead;
  }

  public int getDensity()
  {
    return m_density;
  }

  public long getTotalEnergy()
  {
    return m_totalEnergy;
  }

  public String getAverageDissipatedEnergyPerReceivedPacketPerNode()
  {
    if (m_totalDataReceivedAtSink == 0)
    {
      return "N/A";
    }
    return FORMAT.format((double) m_totalEnergy / m_totalDataReceivedAtSink
        / m_nodes);
  }

  public String getAverageLatency()
  {
    if (m_totalDataReceivedAtSink == 0)
    {
      return "N/A";
    }
    return FORMAT.format((double) m_sumOfLatency / m_totalDataReceivedAtSink);
  }

  public String getPacketDeliveryRatio()
  {
    return FORMAT.format(100 * (double) m_totalDataReceivedAtSink
        / m_totalDataSentAtSource);
  }

  public void gather(AbstractDiffusionNode node)
  {
    m_runTime = System.currentTimeMillis() - m_startTime;
    m_nodes++;
    m_totalEnergy += node.getEnergyConsumed();
    if (node.isSource())
    {
      m_sources++;
      m_totalDataSentAtSource += ((SensorNode) node).getSourcePackets();
      m_totalInterestsSentAtSink += node.getInterestsSent();
    }
    if (node.isSink())
    {
      m_sinks++;
      // m_totalDataReceivedAtSink += node.getDataReceived();
      for (DataMessage data : ((AbstractSinkNode) node).getReceivedData())
      {
        m_sumOfLatency += data.getReceiveTime() - data.getSourceTime();
        m_totalDataReceivedAtSink++;
      }
    }

    if (node instanceof SimpleNodePddd)
    {
      m_ackOverhead += ((SimpleNodePddd) node).getNumAcksSent();
    }

  }
}
